package com.atguigu.tingshu.user.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.WxMaUserService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.cache.GuiGuCache;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.*;
import com.atguigu.tingshu.user.mapper.*;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.user.service.strategy.PaidRecordStrategy;
import com.atguigu.tingshu.user.service.strategy.factory.PaidRecordStrategyFactory;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.net.SocketTimeoutException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;


    @Autowired
    private WxMaService wxMaService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitService rabbitService;


    @Autowired
    private UserPaidAlbumMapper userPaidAlbumMapper;

    @Autowired
    private UserPaidTrackMapper userPaidTrackMapper;

    @Autowired
    private AlbumFeignClient albumFeignClient;

    @Autowired
    private VipServiceConfigMapper vipServiceConfigMapper;

    @Autowired
    private UserVipServiceMapper userVipServiceMapper;

    /**
     * 微信一键登录
     *
     * @param code 调用微信接口使用临时code 有效期5分钟，仅能使用一次
     * @return {token:"令牌"}
     */
    @Override
    public Map<String, String> wxLogin(String code) {
        try {
            //1.根据临时票据对接微信接口获取微信唯一标识OpenId 获取到OpenId意味登录成功
            WxMaUserService userService = wxMaService.getUserService();
            WxMaJscode2SessionResult sessionInfo = userService.getSessionInfo(code);
            String wxOpenid = sessionInfo.getOpenid();
            //2.根据微信唯一标识OpenID查询用户记录,判断是否首次登录true:新增用户绑定微信openId,false:直接登录
            LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UserInfo::getWxOpenId, wxOpenid);
            UserInfo userInfo = userInfoMapper.selectOne(queryWrapper);
            //2.1 TODO 如果用户信息不存在，说明该微信账户首次登录小程序，新增用户记录（绑定微信OPenID），采用MQ异步初始化账户（余额）信息
            if (userInfo == null) {
                //2.1.1 构建用户对象保存用户关联微信openId
                userInfo = new UserInfo();
                userInfo.setWxOpenId(wxOpenid);
                userInfo.setNickname("听友" + IdUtil.fastSimpleUUID());
                userInfo.setAvatarUrl("http://192.168.200.6:9000/tingshu/2024-04-02/0e9cfe8a-9866-4bc5-9a7f-164ed5ef5938.png");
                userInfoMapper.insert(userInfo);

                //2.1.2 发送异步MQ消息初始化账户记录
                Map<String, Object> mqData = new HashMap<>();
                mqData.put("userId", userInfo.getId());
                mqData.put("amount", new BigDecimal("10"));
                mqData.put("title", "注册赠送10元体验金");
                mqData.put("orderNo", "zs" + IdUtil.getSnowflakeNextId());
                rabbitService.sendMessage(MqConst.EXCHANGE_USER, MqConst.ROUTING_USER_REGISTER, mqData);
            }
            //3.为当前登录用户生成token，将用户基本信息存入Redis
            //3.1 采用UUID生成登录成功令牌
            String token = IdUtil.randomUUID();
            String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
            //3.2 将用户基本信息UserInfoVo存入Redis
            UserInfoVo userInfoVo = BeanUtil.copyProperties(userInfo, UserInfoVo.class);
            redisTemplate.opsForValue().set(loginKey, userInfoVo, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);
            //3.3 将令牌存入Map集合,响应给客户端小程序
            Map<String, String> map = new HashMap<>();
            map.put("token", token);
            return map;
        } catch (Exception e) {
            log.error("[用户服务]微信登录异常：{}", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据用户ID查询用户信息
     *
     * @param userId
     * @return
     */
    @Override
    @GuiGuCache(prefix = "user:info:")
    public UserInfoVo getUserInfo(Long userId) {
        UserInfo userInfo = userInfoMapper.selectById(userId);
        return BeanUtil.copyProperties(userInfo, UserInfoVo.class);
    }

    /**
     * 更新用户信息(只允许更新基本信息)
     *
     * @param userId
     * @param userInfoVo
     */
    @Override
    public void updateUser(Long userId, UserInfoVo userInfoVo) {
        UserInfo userInfo = new UserInfo();
        userInfo.setId(userId);
        userInfo.setNickname(userInfoVo.getNickname());
        userInfo.setAvatarUrl(userInfoVo.getAvatarUrl());
        userInfoMapper.updateById(userInfo);
    }

    /**
     * 判断指定专辑下声音ID购买状态
     *
     * @param userId                     用户ID
     * @param albumId                    专辑ID
     * @param needCheckPayStatusTrackIds 专辑某页需要检查购买状态声音ID列表
     * @return {10001:1,10002:0,待检测声音ID：购买状态}
     */
    @Override
    public Map<Long, Integer> userIsPaidTrack(Long userId, Long albumId, List<Long> needCheckPayStatusTrackIds) {
        Map<Long, Integer> map = new HashMap<>();
        //1. 根据用户ID+专辑ID查询已购专辑表
        Long count = userPaidAlbumMapper
                .selectCount(new LambdaQueryWrapper<UserPaidAlbum>().eq(UserPaidAlbum::getUserId, userId).eq(UserPaidAlbum::getAlbumId, albumId));
        //2. 如果存在购买专辑记录，将待检查声音ID购买状态全部设置为1返回
        if (count > 0) {
            for (Long needCheckPayStatusTrackId : needCheckPayStatusTrackIds) {
                map.put(needCheckPayStatusTrackId, 1);
            }
            return map;
        }
        //3. 根据用户ID+专辑ID查询已购声音表
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper
                .selectList(new LambdaQueryWrapper<UserPaidTrack>().eq(UserPaidTrack::getUserId, userId).eq(UserPaidTrack::getAlbumId, albumId));

        //4. 如果不存在已购声音记录，将待检查声音ID购买状态全部设置为0返回
        if (CollectionUtil.isEmpty(userPaidTrackList)) {
            for (Long needCheckPayStatusTrackId : needCheckPayStatusTrackIds) {
                map.put(needCheckPayStatusTrackId, 0);
            }
            return map;
        }
        //5. 如果存在已购声音记录，则判断声音ID是否在已购声音ID列表中，如果在，则设置购买状态为1，否则设置为0，返回
        //5.1 将已购声音集合泛型改为已购声音ID集合
        List<Long> userPaidTrackIdList = userPaidTrackList.stream()
                .map(UserPaidTrack::getTrackId)
                .collect(Collectors.toList());
        for (Long needCheckPayStatusTrackId : needCheckPayStatusTrackIds) {
            if (userPaidTrackIdList.contains(needCheckPayStatusTrackId)) {
                map.put(needCheckPayStatusTrackId, 1);
            } else {
                map.put(needCheckPayStatusTrackId, 0);
            }
        }
        return map;
    }

    /**
     * 判断当前用户是否购买过指定专辑
     *
     * @param albumId
     * @param userId
     * @return
     */
    @Override
    public Boolean isPaidAlbum(Long albumId, Long userId) {
        LambdaQueryWrapper<UserPaidAlbum> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserPaidAlbum::getAlbumId, albumId);
        queryWrapper.eq(UserPaidAlbum::getUserId, userId);
        Long count = userPaidAlbumMapper.selectCount(queryWrapper);
        return count > 0;
    }

    /**
     * 获取当前用户指定专辑下已购声音ID列表
     *
     * @param albumId 专辑ID
     * @param userId  用户ID
     * @return
     */
    @Override
    public List<Long> getUserPaidTrackList(Long albumId, Long userId) {
        //1.构建查询对象 封装查询条件：用户ID+专辑ID  只需要声音ID列
        LambdaQueryWrapper<UserPaidTrack> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserPaidTrack::getUserId, userId);
        queryWrapper.eq(UserPaidTrack::getAlbumId, albumId);
        queryWrapper.select(UserPaidTrack::getTrackId);
        //2.获取已购声音ID列表
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(queryWrapper);
        if (CollectionUtil.isNotEmpty(userPaidTrackList)) {
            List<Long> userPaidTrackIdList = userPaidTrackList.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
            return userPaidTrackIdList;
        }
        return null;
    }

    @Autowired
    private PaidRecordStrategyFactory paidRecordStrategyFactory;

    /**
     * 用户付款成功后（余额、微信），进行虚拟物品（会员、专辑、声音）发货
     *
     * @param userPaidRecordVo
     * @return
     */
    @Override
    public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
        //购买项目类型 1001-专辑 1002-声音 1003-vip会员
        String itemType = userPaidRecordVo.getItemType();
        PaidRecordStrategy strategy = paidRecordStrategyFactory.getStrategy(itemType);
        strategy.handlerPaidRecord(userPaidRecordVo);
    }

    /**
     * 更新VIP状态：处理过期会员
     *
     * @return
     */
    @Override
    public void updateVipExpireStatus(Date date) {
        //1.查询所有会员时间到期用户列表
        LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserInfo::getIsVip, 1);
        queryWrapper.le(UserInfo::getVipExpireTime, date);
        queryWrapper.select(UserInfo::getId);
        List<UserInfo> userInfoList = userInfoMapper.selectList(queryWrapper);
        //2.更新已到期会员标识为0
        if (CollectionUtil.isNotEmpty(userInfoList)) {
            for (UserInfo userInfo : userInfoList) {
                userInfo.setIsVip(0);
                userInfoMapper.updateById(userInfo);
            }
        }
    }
}
